package com.csizg.facerecognition;

import android.app.Activity;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Bundle;
import android.util.Log;
import com.csizg.facerecognition.seeta.FaceDetector2;
import com.csizg.facerecognition.seeta.FaceRecognizer2;
import com.csizg.facerecognition.seeta.PointDetector2;
import com.csizg.facerecognition.seeta.SeetaImageData;
import com.csizg.facerecognition.seeta.SeetaPointF;
import com.csizg.facerecognition.seeta.SeetaRect;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.ByteBuffer;

/**
 * @author haohao(ronghao3508@gmail.com) on 2018/5/12 20:11
 * @version v1.0
 */
public class SeetaActivity extends Activity {
    private FaceDetector2 faceDetector;
    private PointDetector2 pointDetector;
    private FaceRecognizer2 faceRecognizer;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.face_detect_seeta_view);
        initView();
    }

    private void initView() {
        loadModel();
        loadPointDetectorModel();
        loadFaceRecognizerModel();
        recognize();
    }

    private void recognize() {
        SeetaImageData image = ConvertToSeetaImageData("");
        SeetaPointF[] landmarks = getData(image);
        int registedFaceIndex = faceRecognizer.Register(image, landmarks);
        float[] similarity = new float[1];//save the most similar face similarity value
        int targetIndex = faceRecognizer.Recognize(image, landmarks,
                similarity);//targetIndex is the index of the most similar face in database
        float[] crossSimilarities = faceRecognizer.RecognizeEx(image,
                landmarks);//crossSimilarities's length is the returned value of MaxRegisterIndex()
    }

    private SeetaPointF[] getData(SeetaImageData image) {
        SeetaRect[] seetaRects = new SeetaRect[1];
        SeetaPointF[] landmarks = pointDetector.Detect(image,
                seetaRects[0]);//if seetaRects not empty，seetaRects[0] is maximum face
        return landmarks;
    }

    private SeetaPointF[] getData(String path) {
        SeetaRect[] seetaRects = new SeetaRect[1];
        SeetaImageData image = ConvertToSeetaImageData(path);
        SeetaPointF[] landmarks = pointDetector.Detect(image,
                seetaRects[0]);//if seetaRects not empty，seetaRects[0] is maximum face
        return landmarks;
    }

    private void loadModel() {
        try {
            // load cascade file from application resources
            InputStream is = getResources().openRawResource(R.raw.lbpcascade_frontalface);
            File cascadeDir = getDir("cascade", Context.MODE_PRIVATE);
            File mCascadeFile = new File(cascadeDir, "lbpcascade_frontalface.xml");
            FileOutputStream os = new FileOutputStream(mCascadeFile);

            byte[] buffer = new byte[4096];
            int bytesRead;
            while ((bytesRead = is.read(buffer)) != -1) {
                os.write(buffer, 0, bytesRead);
            }
            is.close();
            os.close();

            faceDetector = new FaceDetector2(mCascadeFile.getAbsolutePath());

            cascadeDir.delete();
        } catch (IOException e) {
            e.printStackTrace();
            Log.e("", "Failed to load cascade. Exception thrown: " + e);
        }
    }

    public SeetaImageData ConvertToSeetaImageData(String imgPath) {
        Bitmap bmp = BitmapFactory.decodeFile(imgPath);

        Bitmap bmp_src = bmp.copy(Bitmap.Config.ARGB_8888, true); // true is RGBA
        SeetaImageData imageData = new SeetaImageData(bmp_src.getWidth(), bmp_src.getHeight(), 3);
        imageData.data = getPixelsBGR(bmp_src);

        return imageData;
    }

    public byte[] getPixelsBGR(Bitmap image) {
        // calculate how many bytes our image consists of
        int bytes = image.getByteCount();

        ByteBuffer buffer = ByteBuffer.allocate(bytes); // Create a new buffer
        image.copyPixelsToBuffer(buffer); // Move the byte data to the buffer

        byte[] temp = buffer.array(); // Get the underlying array containing the data.

        byte[] pixels = new byte[(temp.length / 4) * 3]; // Allocate for BGR

        // Copy pixels into place
        for (int i = 0; i < temp.length / 4; i++) {

            pixels[i * 3] = temp[i * 4 + 2];        //B
            pixels[i * 3 + 1] = temp[i * 4 + 1];    //G
            pixels[i * 3 + 2] = temp[i * 4];       //R
        }

        return pixels;
    }

    private void loadPointDetectorModel() {
        try {
            // load cascade file from application resources
            InputStream is = getResources().openRawResource(R.raw.lbpcascade_frontalface);
            File cascadeDir = getDir("cascade", Context.MODE_PRIVATE);
            File mCascadeFile = new File(cascadeDir, "lbpcascade_frontalface.xml");
            FileOutputStream os = new FileOutputStream(mCascadeFile);

            byte[] buffer = new byte[4096];
            int bytesRead;
            while ((bytesRead = is.read(buffer)) != -1) {
                os.write(buffer, 0, bytesRead);
            }
            is.close();
            os.close();

            pointDetector = new PointDetector2(mCascadeFile.getAbsolutePath());

            cascadeDir.delete();
        } catch (IOException e) {
            e.printStackTrace();
            Log.e("", "Failed to load cascade. Exception thrown: " + e);
        }
    }

    private void loadFaceRecognizerModel() {
        try {
            // load cascade file from application resources
            InputStream is = getResources().openRawResource(R.raw.lbpcascade_frontalface);
            File cascadeDir = getDir("cascade", Context.MODE_PRIVATE);
            File mCascadeFile = new File(cascadeDir, "lbpcascade_frontalface.xml");
            FileOutputStream os = new FileOutputStream(mCascadeFile);

            byte[] buffer = new byte[4096];
            int bytesRead;
            while ((bytesRead = is.read(buffer)) != -1) {
                os.write(buffer, 0, bytesRead);
            }
            is.close();
            os.close();

            faceRecognizer = new FaceRecognizer2(mCascadeFile.getAbsolutePath());

            cascadeDir.delete();
        } catch (IOException e) {
            e.printStackTrace();
            Log.e("", "Failed to load cascade. Exception thrown: " + e);
        }
    }
}
